package polymonkey.collide;

import processing.core.*;

/**
 * AABB3D (Axis Aligned Bounding Box 3D) is a class that holds the min/max of a rectanglar volume. It has numerous way of knowing if it is colliding with other shapes and the screen.
 * 
 * @author Matt Ditton 
 * @version 1
 *
 */

public class AABB3D
{
	private PApplet parent;

	/**
	 * The minimum point of a AABB3D
	 */
	public PVector min = new PVector();

	/**
	 * The maximum point of a AABB3D
	 */	
	public PVector max = new PVector();

	/**
	 * The default constructor sets a zero volume AABB. Useful for initialising the AABB ready for adding lots of points to it.
	 * @param _parent
	 */
	public AABB3D(PApplet _parent)
	{
		parent = _parent;

		min.set(0, 0, 0);

		max.set(0, 0, 0);
	}

	/**
	 * Constructor for specifying the 2 points of the AABB. The min/max order of the 2 points doesn't matter. It's figured out for you. 
	 * @param _parent
	 * @param x1
	 * @param y1
	 * @param z1
	 * @param x2
	 * @param y2
	 * @param z2
	 */

	public AABB3D(PApplet _parent, float x1, float y1, float z1, float x2, float y2, float z2)
	{
		parent = _parent;
		
		reset();
		
		addPoint(x1, y1, z1);
		
		addPoint(x2, y2, z2);
	}
	
	/**
	 * Constructor for specifying the 2 points of the AABB using PVectors. The min/max order of the 2 points doesn't matter. It's figured out for you.
	 * @param _parent
	 * @param p1
	 * @param p2
	 */
	public AABB3D(PApplet _parent, PVector p1, PVector p2)
	{
		parent = _parent;
		
		reset();
		
		addPoint(p1);
		
		addPoint(p2);
	}

	/**
	 * Used to reset the AABB3D to be inverted (max < min) ready for new Points. Calling reset() and adding Points every frame will result in an AABB3D that tracks multipal moving points. 
	 */
	public void reset()
	{
		min.x = PApplet.MAX_INT;
		min.y = PApplet.MAX_INT;
		min.z = PApplet.MAX_INT;
		max.x = PApplet.MIN_INT;
		max.y = PApplet.MIN_INT;
		max.z = PApplet.MIN_INT;		
	}

	/**
	 * Add points into the AABB to redefine the min or max
	 * @param point
	 */
	public void addPoint(PVector point)
	{
		addPoint(point.x, point.y, point.z);
	}

	/**
	 * Add points into the AABB3D to redefine the min or max
	 * @param x
	 * @param y
	 * @param z 
	 */
	public void addPoint(float x, float y, float z)
	{
		if(x < min.x) {min.x = x;}
		if(x > max.x) {max.x = x;}
		if(y < min.y) {min.y = y;}
		if(y > max.y) {max.y = y;}
		if(z < min.z) {min.z = z;}
		if(z > max.z) {max.z = z;}		
	}

	/**
	 * Moves the center of the box to the PVector location 
	 * @param PVector location of the new center (Z is ignored in AABB3D)
	 */
	public void moveCenterTo(PVector p)
	{
		moveCenterTo(p.x, p.y, p.z);
	}

	/**
	 * Moves the center of the box to the x, y, z location
	 * @param x location of the new center
	 * @param y location of the new center
	 * @param z location of the new center
	 */
	public void moveCenterTo(float x, float y, float z)
	{
		float tx = (max.x - min.x)/2.0f;
		float ty = (max.y - min.y)/2.0f;
		float tz = (max.z - min.z)/2.0f;
		
		translate(x-tx, y-ty, z-tz);
	}	
	
	/**
	 * Adds the PVector to the min and max of the Box. Good for moving a Bounding Box to keep up with an object that doesn't change size.  
	 * @param PVector to move the min and max of the box by.
	 */
	
	public void translate(PVector p)
	{	
		min.add(p);
		max.add(p);
	}


	/**
	 * Adds the x,y to the min and max of the Box. Good for moving a Bounding Box to keep up with an object that doesn't change size.  
	 * @param x amount to move the min max by
	 * @param y amount to move the min max by
	 * @param z amount to move the min max by
	 */

	public void translate(float x, float y, float z)
	{
		min.add(x, y, z);
	    max.add(x, y, z);
	}	
	
	/**
	 * Helper for drawing a rectangle from the min max of the box
	 */
	
	public void draw()
	{
	    parent.pushStyle();
		
	    parent.rectMode(PApplet.CORNERS);
	    
	    parent.noFill();
	    
	    parent.stroke(255, 0, 255);
		
	    parent.rect(min.x, min.y, max.x, max.y);

	    parent.popStyle();
	    
	}
	
	/**
	 * Used to check if the AABB3D is touching the projected volume of a AABB2D. The AABB2D is assumed to be projected along the z axis.
	 * @param other AABB2D
	 * @return true if colliding
	 */
	public boolean isColliding(AABB2D other)
	{
		if(min.x > other.max.x){return false;}
		if(other.min.x > max.x){return false;}
		if(min.y > other.max.y){return false;}
		if(other.min.y > max.y){return false;}
		return true;
	}
	
	
	/**
	 * Used to check if 2 AABB3D's are touching.
	 * @param other AABB3D
	 * @return true if colliding
	 */
	public boolean isColliding(AABB3D other)
	{
		if(min.x > other.max.x){return false;}
		if(other.min.x > max.x){return false;}
		if(min.y > other.max.y){return false;}
		if(other.min.y > max.y){return false;}
		if(min.z > other.max.z){return false;}
		if(other.min.z > max.z){return false;}		
		return true;
	}

	/**
	 * Used to check if a PVector is inside the AABB3D 
	 * @param PVector position for checking
	 * @return true if colliding
	 */
	public boolean isColliding(PVector p)
	{
		return isColliding(p.x, p.y, p.z);
	}

	/**
	 * Used to check if an x, y, z location is inside the AABB3D
	 * @param x
	 * @param y
	 * @param z
	 * @return true if colliding
	 */
	public boolean isColliding(float x, float y, float z)
	{
		if(min.x > x){return false;}
		if(max.x < x){return false;}
		if(min.y > y){return false;}
		if(max.y < y){return false;}
		if(min.z > z){return false;}
		if(max.z < z){return false;}		
		return true;
	}


	/**
	 * A helper to know if the AABB3D is contacting the left/right edge of the sketch  
	 * @return true if min.x < 0 or max.x > width
	 */

	public boolean hitScreenX()
	{
		return (min.x <= 0.0 || max.x >= parent.width);
	}

	/**
	 * A helper to know if the AABB3D is contacting the up/down edge of the sketch  
	 * @return true if min.y < 0 or max.y > height
	 */	
	public boolean hitScreenY()
	{
		return (min.y <= 0.0 || max.y >= parent.height);
	}

//	private int getAxis(AABB3D other)
//	{
//	    
//		PVector center = new PVector((max.x - min.x)/2.0f, (max.y - min.y)/2.0f);
//		PVector otherCenter = new PVector((other.max.x - other.min.x)/2.0f, (other.max.y - other.min.y)/2.0f);
//		center.sub(otherCenter);
//		center.normalize();
//	    
////		float ofxVec2f::dot( const ofxVec2f& vec ) const {
////			return x*vec.x + y*vec.y;
////		}		
//		
//		// TODO: this is some funky code. need to fix this up tomorrow.
//	    float dots[] = new float[4];
//	    dots[0] = center.x*0 + center.y*1;// dir.dot(VEC2_NORTH);
//	    dots[1] = center.x*0 + center.y*-1;
//	    dots[2] = center.x*1 + center.y*0;
//	    dots[3] = center.x*-1 + center.y*0;
//	   
//	    int lowest = MAX_INT;
//	    float small_dot = MAX_FLOAT;
//	    
//	    for(int i = 0; i < dots.length; i++)
//	    {
//	        if(dots[i] < small_dot)
//	        {
//	            small_dot = dots[i];
//	            lowest = i;
//	        }
//	    }
//	    
//	    return lowest;
//	   
//	}	
	
}